/*
 * This file is part of the cSploit.
 *
 * Copyleft of Massimo Dragano aka tux_mind <tux_mind@csploit.org>
 *
 * cSploit is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * cSploit is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with cSploit.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.csploit.android.plugins;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;

import org.csploit.android.R;
import org.csploit.android.core.Plugin;
import org.csploit.android.core.System;
import org.csploit.android.gui.MsfPreferences;
import org.csploit.android.gui.dialogs.ChoiceDialog;
import org.csploit.android.gui.dialogs.ErrorDialog;
import org.csploit.android.gui.dialogs.FinishDialog;
import org.csploit.android.gui.dialogs.ListChoiceDialog;
import org.csploit.android.net.Target;
import org.csploit.android.net.Target.Exploit;
import org.csploit.android.net.datasource.Search;
import org.csploit.android.net.metasploit.MsfExploit;
import org.csploit.android.net.metasploit.Payload;
import org.csploit.android.net.metasploit.RPCClient;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;

public class ExploitFinder extends Plugin {
    private ToggleButton mSearchToggleButton = null;
    private ProgressBar mSearchProgress = null;
    private ListView mListView = null;
    private Future job = null;
    private ExploitAdapter mAdapter = null;

    private static ExploitFinder UIThread = null;

    public class ExploitAdapter extends ArrayAdapter<Exploit> {
        class ExploitHolder {
            ImageView itemImage;
            TextView itemTitle;
            TextView itemDescription;
        }

        public ExploitAdapter() {
            super(ExploitFinder.this, R.layout.plugin_exploit_finder_item, (List<Exploit>) System.getCurrentExploits());
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View row = convertView;
            ExploitHolder holder;

            if (row == null) {
                LayoutInflater inflater = (LayoutInflater) ExploitFinder.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                row = inflater.inflate(R.layout.plugin_exploit_finder_item, parent, false);

                holder = new ExploitHolder();

                holder.itemImage = (ImageView) row.findViewById(R.id.itemIcon);
                holder.itemTitle = (TextView) row.findViewById(R.id.itemTitle);
                holder.itemDescription = (TextView) row.findViewById(R.id.itemDescription);

                row.setTag(holder);
            } else {
                holder = (ExploitHolder) row.getTag();
            }

            Exploit exploit = getItem(position);

            if (exploit instanceof MsfExploit) {
                holder.itemTitle.setText
                        (
                                Html.fromHtml
                                        (
                                                "<b>" + exploit.getName() + "</b>"
                                        )
                        );
            } else
                holder.itemTitle.setText(exploit.getName());

            holder.itemTitle.setTextColor(getResources().getColor(
                    exploit.isEnabled() ? R.color.app_color : R.color.gray_text));
            holder.itemTitle.setTypeface(null, Typeface.NORMAL);
            holder.itemImage.setImageResource(exploit.getDrawableResourceId());
            holder.itemDescription.setText(exploit.getSummary());

            return row;
        }
    }

    private OnItemClickListener listener = new OnItemClickListener() {
        public void onItemClick(AdapterView<?> parent, View v, final int position, long id) {
            Exploit exp = mAdapter.getItem(position);

            if (exp == null)
                return;

            if (exp instanceof MsfExploit) {
                final MsfExploit msfEx = (MsfExploit) exp;
                final ArrayList<Integer> availableChoices = new ArrayList<Integer>();

                if (System.getMsfRpc() != null && !System.getMsfRpc().isRemote())
                    availableChoices.add(R.string.exploit_launch);
                if (msfEx.getOptions() != null)
                    availableChoices.add(R.string.exploit_edit_options);
                if (msfEx.getCurrentPayload() != null)
                    availableChoices.add(R.string.payload_edit_settings);
                if (msfEx.getPayloads() != null)
                    availableChoices.add(R.string.exploit_choose_payload);
                if (msfEx.getTargets() != null)
                    availableChoices.add(R.string.exploit_choose_target);
                if (msfEx.getUrl() != null && !msfEx.getUrl().isEmpty())
                    availableChoices.add(R.string.open_url);
                if (msfEx.getDescription() != null)
                    availableChoices.add(R.string.show_full_description);
                new ListChoiceDialog(R.string.choose_an_option, availableChoices.toArray(new Integer[availableChoices.size()]), ExploitFinder.this, new ChoiceDialog.ChoiceDialogListener() {
                    @Override
                    public void onChoice(int choice) {
                        Intent intent;
                        switch (availableChoices.get(choice)) {
                            case R.string.exploit_launch:
                                new Thread(new Runnable() {
                                  @Override
                                  public void run() {

                                    int length;
                                    String message;

                                    length = Toast.LENGTH_SHORT;

                                    try {
                                      if (msfEx.launch())
                                        message = "Job started";
                                      else
                                        message = "launch failed";
                                    } catch (RPCClient.MSFException e) {
                                      message = String.format("launch failed: %s", e.getMessage());
                                      length = Toast.LENGTH_LONG;
                                    }

                                    final int flength = length;
                                    final String fmessage = message;

                                    ExploitFinder.this.runOnUiThread(new Runnable() {
                                      @Override
                                      public void run() {
                                        Toast.makeText(ExploitFinder.this, fmessage, flength).show();
                                      }
                                    });
                                  }
                                }).start();
                                break;
                            case R.string.exploit_edit_options:
                                intent = new Intent(ExploitFinder.this, MsfPreferences.class);
                                System.setCurrentExploit(msfEx);
                                ExploitFinder.this.startActivity(intent);
                                break;
                            case R.string.payload_edit_settings:
                                intent = new Intent(ExploitFinder.this, MsfPreferences.class);
                                System.setCurrentPayload(msfEx.getCurrentPayload());
                                ExploitFinder.this.startActivity(intent);
                                break;
                            case R.string.exploit_choose_payload:
                                new ListChoiceDialog("Choose a payload", msfEx.getPayloads().toArray(new Payload[msfEx.getPayloads().size()]), ExploitFinder.this, new ChoiceDialog.ChoiceDialogListener() {
                                    @Override
                                    public void onChoice(int choice) {
                                        msfEx.setPayload(choice);
                                    }
                                }).show();
                                break;
                            case R.string.exploit_choose_target:
                                new ListChoiceDialog("Choose a target", msfEx.getTargets().toArray(new String[msfEx.getTargets().size()]), ExploitFinder.this, new ChoiceDialog.ChoiceDialogListener() {
                                    @Override
                                    public void onChoice(int choice) {
                                        msfEx.setTarget(choice);
                                    }
                                }).show();
                                break;
                            case R.string.show_full_description:
                                new ErrorDialog(msfEx.getName(), msfEx.getDescription(), ExploitFinder.this).show();
                                break;
                            case R.string.open_url:
                                startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(msfEx.getUrl())));
                                break;
                        }
                    }
                }).show();
            } else {
                if (exp.getUrl() != null && !exp.getUrl().isEmpty())
                    startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(exp.getUrl())));
            }
        }
    };

    public ExploitFinder() {
        super
                (

                        R.string.exploit_finder,
                        R.string.exploit_finder_desc,

                        new Target.Type[]{Target.Type.ENDPOINT, Target.Type.REMOTE},
                        R.layout.plugin_exploit_finder,
                        R.drawable.action_exploit_finder
                );
    }

    private void setStartedState() {
        mSearchProgress.setVisibility(View.VISIBLE);

        final Target target = System.getCurrentTarget();

        job = Search.searchExploitForServices(target,
                new Search.Receiver<Exploit>() {
                    private boolean somethingFound = false;

                    @Override
                    public void onItemFound(final Exploit exploit) {
                        somethingFound = true;
                        ExploitFinder.this.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                target.addExploit(exploit);
                                mAdapter.notifyDataSetChanged();
                            }
                        });
                    }

                    @Override
                    public void onFoundItemChanged(Exploit exploit) {
                        ExploitFinder.this.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                mAdapter.notifyDataSetChanged();
                            }
                        });
                    }

                    @Override
                    public void onEnd() {
                        ExploitFinder.this.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                mSearchProgress.setVisibility(View.GONE);
                                mSearchToggleButton.setChecked(false);
                                if (System.getCurrentExploits().size() == 0) {
                                    new FinishDialog(getString(R.string.warning), getString(R.string.no_exploits_found), ExploitFinder.this).show();
                                } else if (!somethingFound) {
                                    new ErrorDialog(getString(R.string.warning), getString(R.string.no_exploits_found), ExploitFinder.this).show();
                                }
                            }
                        });
                    }
                });
    }

    private void setStoppedState() {
        if (job != null)
            job.cancel(true);

        mSearchProgress.setVisibility(View.GONE);
        mSearchToggleButton.setChecked(false);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.exploit_finder, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        SharedPreferences themePrefs = getSharedPreferences("THEME", 0);
        Boolean isDark = themePrefs.getBoolean("isDark", false);
        if (isDark)
            setTheme(R.style.DarkTheme);
        else
            setTheme(R.style.AppTheme);
        super.onCreate(savedInstanceState);

        Target t = System.getCurrentTarget();

        if (!t.hasOpenPorts())
            new FinishDialog(getString(R.string.warning), getString(R.string.no_open_ports), this).show();

        else if (!t.hasOpenPortsWithService())
            new FinishDialog(getString(R.string.warning), getString(R.string.no_infos_on_target), this).show();

        mSearchToggleButton = (ToggleButton) findViewById(R.id.searchToggleButton);
        mSearchProgress = (ProgressBar) findViewById(R.id.searchActivity);
        mListView = (ListView) findViewById(android.R.id.list);
        mAdapter = new ExploitAdapter();

        UIThread = this;

        mListView.setAdapter(mAdapter);


        mListView.setOnItemClickListener(listener);

        mSearchToggleButton.setOnClickListener(new OnClickListener() {
                                                   @Override
                                                   public void onClick(View v) {
                                                       if (((ToggleButton) v).isChecked())
                                                           setStartedState();
                                                       else
                                                           setStoppedState();
                                                   }
                                               }
        );
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.exploit_launch_all:
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onBackPressed() {
        setStoppedState();
        super.onBackPressed();
        overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_left);
    }

    @Override
    public void onRpcChange(RPCClient currentValue) {
        // TODO: use Java Observer/Observable implementation
        if (UIThread == null)
            return;
        // rebuild view
        if (this != UIThread) {
            UIThread.onRpcChange(currentValue);
        } else {
            ExploitFinder.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mAdapter.notifyDataSetChanged();
                }
            });
        }
    }
}